#ifndef EVM_CONNECTION_H
#define EVM_CONNECTION_H

#include <array>
#include <condition_variable>

#include "parser.h"
#include "request.h"
#include "response.h"
#include "timer_queue.h"
#include "event/event.h"
#include "common/log.h"
#include "common/exception.h"


namespace evm {
    namespace common {
        class ThreadPool;
    }

    namespace http {
        using namespace std;
        using namespace event;

        using Handler = std::function<void (const Request &, Response&)>;

        class HTTPServer;
        class DateStr;

        static constexpr int KEEPALIVE_TIMEOUT = 5;

        struct Buffer {
            Buffer(int fd) : fd_(fd) {}

            void reserve(size_t rsize) {
                bufs_.reserve(rsize);
            }

            void emplace_back(char* data, size_t size) {
                bufs_.emplace_back(data, size);
            }

            void push_back(const string& data) {
                bufs_.push_back(data);
            }

            void push_back(string&& data) {
                bufs_.push_back(std::move(data));
            }

            bool empty() {
                return bufs_.empty();
            }

            void clear() {
                bufs_.clear();
                vpos_ = 0;
                spos_ = 0;
            }

            bool write() {
                for (; vpos_ != bufs_.size(); ++vpos_) {
                    size_t sremain = bufs_[vpos_].size() - spos_;
                    ssize_t swritten = writeFull(fd_, bufs_[vpos_].data() + spos_, sremain);
                    if (swritten == -1) {
                        if (errno == EAGAIN)  // write EAGAIN
                            swritten = 0;
                        else  // write error
                            return false;
                    }

                    if (static_cast<size_t>(swritten) < sremain) {
                        spos_ += swritten;
                        break;  // write eof
                    }
                    else {
                        spos_ = 0;
                    }
                }

                if (!bufs_.empty() && vpos_ == bufs_.size())
                    clear();

                return true;
            }

            int fd_;
            vector<string> bufs_;
            size_t vpos_{};
            size_t spos_{};
        };

        enum {
            FR_REDUNDANT, FR_NEEDMORE, FR_FINISHED, FR_ERROR
        };

        struct FileReciever {
            FileReciever(size_t len) : len_(len) {}

            void set_file(shared_ptr<File> file) {
                file_ = file;
            }

            int recieve(const char *buf, size_t count) {
                log_info("recieve: {}", count);
                if (count > (len_ - written_))
                    return FR_REDUNDANT;  // invalid request body stream
                if (!file_->write(buf, count)) {
                    return FR_ERROR;  // write file error
                }
                written_ += count;
                if (written_ == len_)
                    return FR_FINISHED;  // finished writing
                return FR_NEEDMORE;  // need more recieving
            }

            void hold(const char *buf, size_t count) {
                buffered_ = true;
                buffer_ = buf;
                buffer_len_ = count;
            }

            int recieve() {
                if (buffered_) {
                    buffered_ = false;
                    return recieve(buffer_, buffer_len_);
                }
                return FR_NEEDMORE;
            }

            /*size_t recieved() {
                return written_;
            }

            size_t remain() {
                return len_ - written_;
            }*/

            bool finished() {
                return written_ == len_;
            }

            shared_ptr<File> file_;
            size_t len_;
            size_t written_{};

            bool buffered_{};
            const char *buffer_;
            size_t buffer_len_;
        };

        class HTTPConnection {
        public:
            HTTPConnection(int fd, HTTPServer* server);

            ~HTTPConnection();

            void start();

        private:

            bool handle_header();

            void recieve_stream(bool begin = false);
            void send_stream();

            void handle_body();

            void respond();

            void do_read();

            void do_write();

            void destroy();

            void start_deadline_timer();
            void cancel_deadline_timer();

            static constexpr int RW_BYTES = 65536;

            enum {
                NORMAL, RECIEVE_STREAM, CLIENT_ERROR, SERVER_ERROR, FINISHED, ASYNC_HANDLE
            } read_state_{NORMAL};

            int fd_;
            HTTPServer *server_;
            EventLoop* loop_;
            common::ThreadPool* pool_;
            DateStr& date_str_;
            TimerQueue& timer_queue_;
            uint64_t timer_key_;
            Parser parser_;

            Request request_;
            Response response_;

            recursive_mutex read_mutex_;
            atomic<bool> readable_{};
            condition_variable_any can_read_;
            char rbuffer_[RW_BYTES];
            shared_ptr<File> recv_file_;
            shared_ptr<FileReciever> fr_;

            recursive_mutex write_mutex_;
            atomic<bool> writeable_{};
            atomic<bool> is_writing_{};
            atomic<bool> need_write_{};
            Buffer wbuffer_;
            shared_ptr<File> send_file_;
            off_t offset_{};

            bool close_connection_{};

            friend class Parser;
        };

    }
}

#endif //EVM_CONNECTION_H
